using Digitalmes.Application.Scada.Extensions;
using Digitalmes.Application.Scada.Models;
using Digitalmes.Application.Services;
using Digitalmes.Domain.Entities.Mes.Processes;
using Digitalmes.Domain.Entities.Mes.Productions;

namespace Digitalmes.Application.Scada.Services.Impl;

/// <summary>
/// 工步过点服务。
/// </summary>
internal sealed class StepTaskService(
    IRepository<ProdCurrentState> currentStateRepo,
    IRepository<ProdInbound> inboundRepo,
    IRepository<ProcRoutingStep> routeStepRepo,
    IRepository<ProcStepTask> stepTaskRepo,
    IRepository<ProdStepTaskExecution> executionRepo,
    IRepository<ProdStepTaskData> stepTaskDataRepo,
    IWorkstationQueryService workstationQueryService) : IStepTaskService
{
    public async Task<ScadaResult> StepAsync(StepTaskInput input, CancellationToken cancellationToken = default)
    {
        // 检索工序信息
        var (operation, _) = await workstationQueryService.GetOperationAsync(input.LineCode, input.WsCode);
        if (operation == null)
        {
            return ScadaResult.Error(ErrorCode.E1303);
        }

        // 找到产品在当前工位信息
        var currentState = await currentStateRepo.FirstOrDefaultAsync(m => m.Sn == input.SN && m.LineId == operation.LineId && m.OperationId == operation.Id, cancellationToken);
        if (currentState == null)
        {
            return ScadaResult.Error(ErrorCode.E1304);
        }

        if (currentState.Stage is WorkStageEnum.Out)
        {
            return ScadaResult.Error(ErrorCode.E1305);
        }

        // 找到产品进站信息
        // 找到对应的工艺步骤
        var inbound = await inboundRepo.LastOrDefaultAsync(m => m.Sn == currentState.Sn && m.OperationId == currentState.OperationId, cancellationToken);
        if (inbound == null)
        {
            return ScadaResult.Error(ErrorCode.E1306);
        }

        // 找到工步及其标准设定
        var routeStep = await routeStepRepo.FirstOrDefaultAsync(m => m.RoutingId == currentState.RoutingId && m.OperationId == currentState.OperationId, cancellationToken);
        var stepTask = await stepTaskRepo.AsQueryable()
            .Includes(m => m.Standards)
            .FirstOrDefaultAsync(m => m.StepId == routeStep!.Id && m.Seq == input.Index, cancellationToken);
        if (stepTask == null)
        {
            return ScadaResult.Error(ErrorCode.E1307);
        }

        // 工步过点记录
        ProdStepTaskExecution stepTaskExecution = new()
        {
            InboundId = inbound.Id,
            Sn = currentState.Sn,
            RoutingId = currentState.RoutingId,
            OperationId = currentState.OperationId,
            WorkstationId = currentState.WorkstationId,
            StepId = stepTask.StepId,
            StepTaskId = stepTask.Id,
            StepTaskCode = stepTask.TaskCode,
            StepTaskName = stepTask.TaskName,
            StartTime = DateTime.Now,
            EndTime = DateTime.Now,
            PassResult = input.PassResult ?? PassEnum.OK, // 过站结果设定
            StandardDuration = stepTask.StandardTime,
            CycleTime = input.CycleTime ?? 0,
        };
        var stepExecutionId = await executionRepo.InsertReturnSnowflakeIdAsync(stepTaskExecution, cancellationToken);

        // 根据标准设定找到对应的采集数据，未找到采集值时不记录。
        var standards = stepTask.Standards;
        if (standards.Count > 0)
        {
            List<ProdStepTaskData> stepTaskDataList = [];
            foreach (var standard in standards)
            {
                var data = input.DataList.FirstOrDefault(s => s.TagName == standard.Symbol);
                if (data != null)
                {
                    // 格式说明：
                    // 上下限优先以设备当前设定的值为准，若设备没设定则按照系统工艺标准配置。
                    // 标记命名格式约定：
                    // 下限值 --> "[主数据信号点]_MixLimit"（标准项中不应该包含单独的"最小"值）;
                    // 上限值 --> "[主数据信号点]_MaxLimit"（同上）;
                    // 结果值 --> "[主数据信号点]_Result"（1->OK;2->NG，同样标准项中不应该包含单独的"结果"值）。
                    ProdStepTaskData archive = new()
                    {
                        ExecutionId = stepExecutionId,
                        Index = stepTask.Seq,
                        ParamName = standard.ParameterName,
                        ParamCode = standard.ParameterCode,
                        Value = NumberUtils.FactorConvertTo(data.GetAsDouble(), standard.Conversion, standard.Digits),
                        Tolerance = standard.Tolerance,
                        MinLimit = NumberUtils.FactorConvertTo(input.DataList.GetValueAsDouble($"{standard.Symbol}_MixLimit"), standard.Conversion, standard.Digits) ?? standard.MinLimit,
                        MaxLimit = NumberUtils.FactorConvertTo(input.DataList.GetValueAsDouble($"{standard.Symbol}_MaxLimit"), standard.Conversion, standard.Digits) ?? standard.MaxLimit,
                        Unit = standard.MeasurementUnit,
                    };
                    stepTaskDataList.Add(archive);

                    // 工步级别标准按工步设定的"是否检查工艺标准"上/下限判定。
                    if (stepTask.IsCheckStandard)
                    {
                        // 校验工艺标准，上限或下限没有值则对应值不进行比较，只比较存在的那方
                        archive.Result = (!archive.MinLimit.HasValue || archive.MinLimit <= archive.Value) && (!archive.MaxLimit.HasValue || archive.Value <= archive.MaxLimit)
                            ? "OK"
                            : "NG";
                    }
                    else
                    {
                        // 如果参数有对应的设定结果值，优先按此结果设定
                        var result = input.DataList.GetValueAsInt32($"{standard.Symbol}_Result");
                        if (result.HasValue)
                        {
                            archive.Result = result switch
                            {
                                1 => "OK",
                                2 => "NG",
                                _ => "",
                            };
                        }
                        else
                        {
                            // 根据设备给的结果指定判定（没有结果则为空），一次取值设备结果只有一个，有多个参数时都设定这个结果
                            archive.Result = input.PassResult.HasValue
                                ? (input.PassResult is PassEnum.OK or PassEnum.ForceOK ? "OK" : "NG")
                                : "";
                        }
                    }
                }
            }

            if (stepTaskDataList.Count > 0)
            {
                await stepTaskDataRepo.InsertRangeAsync(stepTaskDataList, cancellationToken);
            }

            // 若工步（或工艺步骤）是由上位系统校验参数标准是否合格，则检查是否存在校验结果为 NG 的项并告知下位系统来判定不合格。
            if (stepTask.IsCheckStandard && stepTaskDataList.Any(s => s.Result == "NG"))
            {
                return ScadaResult.Error(ErrorCode.E1308);
            }
        }

        return ScadaResult.Ok();
    }
}
